package per.jdz.core;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import per.jdz.annotation.After;
import per.jdz.annotation.Around;
import per.jdz.annotation.Before;
import per.jdz.annotation.Throwing;

public class AopProxy implements MethodInterceptor{

    Object aopObject;

    Method beforMethod;
    Method afterMethod;
    Method throwingMethod;
    Method aroundMethod;
    Map<Integer, Method> interceptorMethods = new HashMap<Integer, Method>();

    boolean isInterceptorAll = false;

    public AopProxy() {
    }

    public void setEnhancerMethods(Object aopObject) {
        for (Method method : aopObject.getClass().getDeclaredMethods()) {
            if (method.isAnnotationPresent(Before.class)) {
                beforMethod = method;
            } else if (method.isAnnotationPresent(After.class)) {
                afterMethod = method;
            } else if (method.isAnnotationPresent(Throwing.class)) {
                throwingMethod = method;
            } else if (method.isAnnotationPresent(Around.class)) {
                aroundMethod = method;
            }
        }
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        final int hashcode = method.hashCode();
        // 前后置拦截器入参分别为 JoinPoint
        // 环绕为 ProceedingJoinPoint 因为环绕有 invoke 行为
        if (isInterceptorAll || interceptorMethods.containsKey(hashcode)) {
            final JoinPoint joinPoint = new JoinPoint(method, objects);
            Object ret = null;
            try {
                invokeMethod(joinPoint, beforMethod);
                // 如果有环绕通知，则执行目标方法由环绕通知执行
                if (aroundMethod != null) {
                    ret = invokeMethod(new ProceedingJoinPoint(o, methodProxy, method, objects), aroundMethod);
                } else {
                    ret = methodProxy.invokeSuper(o, objects);
                }
                return ret;
            } catch (Throwable throwable) {
                throwable.printStackTrace();
                invokeMethod(throwable, throwingMethod);
                return ret;
            } finally {
                invokeMethod(joinPoint, afterMethod);
            }
        }
        return methodProxy.invokeSuper(o, objects);
    }
    
    private Object invokeMethod(Object joinPoint, Method method) throws Throwable{
        Object ret = null;
        if (method != null) {
            // 判断执行方法上面是否有目标参数
            if (method.getParameterTypes().length > 0) {
                if (!method.getParameterTypes()[0].equals(joinPoint.getClass())) {
                    throw new IllegalArgumentException("参数映射错误:非 IJoinPoint 参数");
                }
                ret = method.invoke(aopObject, joinPoint);
            } else {
                ret = method.invoke(aopObject);
            }
        }
        return ret;
    }
}
